local oo = require("SharedLibs.BaseLib");
local co = require("SharedLibs.CoroLib");
local CombinedOp = require("SharedLibs.CombinedOp");
Debugger = require("SharedLibs.Debugger");

Seq = require("SharedLibs.FP.Sequence");

---
local Sequence = oo.Class()
Sequence.name = "Sequence"
oo.ClassExtends(Sequence, CombinedOp)

---
function co.Sequence(...)
	local seq = oo.New(Sequence, ...);
	return co.yield(seq);
end

function co.NewSequence(...)
	return oo.New(Sequence, ...);
end

---
function Sequence.prototype.constructor(self, ...)
	Sequence._ss.prototype.constructor(self, ...)
	self._iCursor = 0; 
	self._onOperationDone = nil;
end

function Sequence.prototype.OnOperationDone(self, onOperationDone)
	self._onOperationDone = onOperationDone;
end

function Sequence.prototype:_onStart(continuation, parent)
	if 0 == #self._opList then 
		self:_doStop(true);
		return;
	else
		self._iCursor = 0;
		self:_moveNext(nil);
	end
end

function Sequence.prototype._moveNext(self, op)	
	local needContinue = true;
	
	while needContinue do
		if self._state == co.AsyncOpState.Stopped then return end;	
		Debugger.Assert(self._state ~= co.AsyncOpState.Ready);
		Debugger.Assert(self._iCursor <= #self._opList);
		
		needContinue = false;
		
		if self._iCursor ~= 0 then
			local lastOp = self._opList[self._iCursor];
			Debugger.Assert(lastOp == op);
			Debugger.Assert(lastOp:IsDone());
			if self._onOperationDone ~= nil then

				self._onOperationDone(self, self._iCursor);
				if self._state == co.AsyncOpState.Stopped then return end
			end
		end

		self._iCursor = self._iCursor+1;
		if self._iCursor <= #self._opList then

			local newCurOp = self._opList[self._iCursor];
			Debugger.Assert(newCurOp.state == co.AsyncOpState.Ready
				, "AsyncOp's state is not Ready! AsyncOp: "..newCurOp:GetDesc()
					.." , current state: "..newCurOp.state);
			newCurOp:Start(self._moveNext, self, true);

			if self._state == co.AsyncOpState.Stopped then return end;
			needContinue = newCurOp:IsDone();
			if needContinue then
				op = newCurOp;
			end
		else
			self._ret = op:GetReturn();
			self:_doStop(true);
		end
	end

end

function Sequence.prototype:GetCurOps()
	return Seq.fromYield(function()
		if self._iCursor ~= 0 then 
			local op = self._opList[self._iCursor];
			assert(op.state == co.AsyncOpState.Executing or op.state == co.AsyncOpState.Paused);
			coroutine.yield(op);
		end
	end);
end

function Sequence.prototype:GetReturn()
	return self._ret;
end

function Sequence.prototype:_onReset()
	self._ret = nil;
	self:_resetOps();
end

function Sequence.prototype:_onStop(isComplete)
	Sequence._ss.prototype._onStop(self, isComplete)
	self._iCursor = 0;
end

return Sequence;	